1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 module linker.Loader; 20 21 import std.algorithm : canFind; 22 import std.stdio; 23 import std.string; 24 25 import linker.Exception; 26 27 public class Linker { 28 /** Contain lib handler by name */ 29 private static void * [string] loadedLibs; 30 /** Contain failed symbols loads by lib */ 31 private static string[][string] failedLoads; 32 33 /** 34 * Function for catching unsopported symbols 35 */ 36 extern(C) static void unsupportedSymbol() { 37 throw new LinkException("The function you are calling is not pressent in your version of lib"); 38 } 39 40 /** 41 * Link the provided symbol 42 * Params: 43 * func = The function we are linking 44 * symbol = Symbol for linking 45 * libs = Libraries for symbol searching 46 */ 47 public static void link(T)(ref T func, string symbol, const string [] libs ...) { 48 func = cast(T)getSymbol(symbol, libs); 49 } 50 51 /** 52 * Get the symbol from the lib 53 * Params: 54 * symbol = Symbol for import 55 * libs = Libraries for symbol searching 56 * Returns: Handle for the symbol 57 */ 58 public static void * getSymbol(string symbol, const string [] libs ...) { 59 void * handle = null; 60 61 foreach (lib; libs) { 62 if (lib !in loadedLibs) loadLibrary(lib); 63 64 handle = getSymbolOS(loadedLibs[lib], symbol); 65 66 if (handle !is null) break; 67 } 68 69 if (handle is null) { 70 foreach (lib; libs) { 71 failedLoads[lib] ~= symbol; 72 } 73 handle = &unsupportedSymbol; 74 } 75 76 return handle; 77 } 78 79 /** 80 * Load a dynamic lib 81 * Params: 82 * library = Library for loading 83 */ 84 public static void loadLibrary(string library) { 85 import std.algorithm.searching : canFind; 86 import std.string : split; 87 88 void * handle = null; 89 90 if (library.canFind(';')) { 91 foreach (lib; library.split(';')) { 92 handle = loadLibraryOS(lib); 93 if (handle !is null) break; 94 } 95 } 96 else handle = loadLibraryOS(library); 97 98 if (handle is null) throw new LinkException("Library load failed ("~ library ~"): "~ getLastErrorMessageOS()); 99 100 loadedLibs[library] = handle; 101 } 102 103 /** 104 * Unload a library 105 * Params: 106 * library = Library for unloading 107 */ 108 public static void unloadLibrary(string library) { 109 unloadLibraryOS(loadedLibs[library]); 110 loadedLibs.remove(library); 111 } 112 113 /** 114 * Check load state 115 * Returns: True if was load fails 116 */ 117 public static bool isFails() { 118 return failedLoads.length != 0; 119 } 120 121 /** 122 * Getter for loaded libs 123 * Returns: Loaded libs list 124 */ 125 public static string [] getLoaded() { 126 return loadedLibs.keys; 127 } 128 129 /** 130 * Check for lib loading 131 * Params: 132 * lib = Lib for checking 133 * Returns: true if the lib was loaded 134 */ 135 public static bool isLoaded(string lib) { 136 if(lib in loadedLibs) return true; 137 return false; 138 } 139 140 /** 141 * Check failed library loads 142 * Params: 143 * lib = Lib for checking 144 * Returns: Failed loads for the library 145 */ 146 public static string [] getFails(string lib) { 147 if (lib in failedLoads) return failedLoads[lib]; 148 return null; 149 } 150 151 /** 152 * Unload all loaded libs at exit 153 */ 154 static ~this() { 155 foreach(lib; loadedLibs.keys) { 156 unloadLibrary(lib); 157 } 158 } 159 160 /** Functions for platform specific library load */ 161 version (Windows) { 162 /** Specific imports */ 163 import core.sys.windows.winnt : IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386; 164 import core.sys.windows.winbase : LoadLibraryA, GetProcAddress, FreeLibrary; 165 166 /** 167 * Load library by using OS-specific functions 168 * Params: 169 * library = Library's for loading name 170 * Returns: Loaded library if everything ok or null in other cases 171 */ 172 private static void * loadLibraryOS(string library) { 173 setDLLPath(); 174 return LoadLibraryA(cast(char *)toStringz(library)); 175 } 176 177 /** 178 * Get symbol from library by using OS-specific functions 179 * Params: 180 * handle = A handle to the DLL module that contains the function or variable 181 * symbol = The function or variable name, or the function's ordinal value 182 * Returns: Address of symbol or null 183 */ 184 private static void * getSymbolOS(void * handle, string symbol) { 185 return GetProcAddress(handle, cast(char *)toStringz(symbol)); 186 } 187 188 /** 189 * Unload library by using OS-specific functions 190 * Params: 191 * lib = Library for unloading 192 * Returns: True if everything is ok 193 */ 194 private static bool unloadLibraryOS(void * lib) { 195 return cast(bool)FreeLibrary(lib); 196 } 197 198 /** 199 * Get the last error message by using OS-specific functions 200 * Returns: String with error message or nothing 201 */ 202 private static string getLastErrorMessageOS() { 203 import core.sys.windows.winbase : GetLastError, FormatMessageA, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_ARGUMENT_ARRAY; 204 import core.sys.windows.winnt : LANG_NEUTRAL; 205 import core.stdc.string : memset; 206 207 char [] buffer = new char[2048]; 208 memset(buffer.ptr, '\0', 2048); 209 210 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 211 null, GetLastError(), LANG_NEUTRAL, buffer.ptr, 2048, cast(char **)["\0".ptr].ptr); 212 213 return fromStringz(buffer.ptr).idup; 214 } 215 216 /** 217 * Say Windows where it should search dll 218 */ 219 private static void setDLLPath() { 220 static bool is_set = false; 221 222 if (is_set) return; 223 224 string gtk_path = findGtkLibs(); 225 226 if (gtk_path !is null) { 227 SetDllDirectoryA((gtk_path ~ '\0').ptr); 228 } 229 230 is_set = true; 231 } 232 233 /** 234 * Find Gtk libs on the computer 235 * Returns: Path to the Gtk libs or null 236 */ 237 private static string findGtkLibs() { 238 import std.algorithm.iteration : splitter; 239 import std.process : environment; 240 import std.path : buildNormalizedPath; 241 import std.file : exists; 242 243 foreach (lib; [`libgtk-3-0.dll`, `libgtk-4-1.dll`, `gtk-3.dll`, `gtk-4.dll`]) { 244 foreach (path; splitter(environment.get("PATH"), ';')) { 245 string dll_path = buildNormalizedPath(path, lib); 246 247 if (exists(dll_path) == false) continue; 248 if (checkDLLArch(dll_path)) return path; 249 } 250 } 251 252 return null; 253 } 254 255 /** 256 * Check a DLL for compatible with the machine 257 * Params: 258 * dll_path = Path to the dll for check 259 * Returns: true if the dll is compatible with the machine 260 */ 261 private static bool checkDLLArch(string dll_path) { 262 import std.stdio : File; 263 264 File dll = File(dll_path); 265 266 dll.seek(0xc3); 267 dll.seek(cast(int)dll.rawRead(new int[1])[0]); 268 269 if (cast(uint)dll.rawRead(new uint[1])[0] != 0x00004550) return false; 270 271 ushort win_type = cast(ushort)dll.rawRead(new ushort[1])[0]; 272 273 version(Win32) { 274 return win_type == IMAGE_FILE_MACHINE_I386; 275 } 276 version(Win64) { 277 return win_type == IMAGE_FILE_MACHINE_AMD64; 278 } 279 } 280 } 281 else { 282 /** Import OS-specific libs */ 283 import core.sys.posix.dlfcn : dlopen, dlerror, dlsym, dlclose; 284 import core.sys.posix.dlfcn : RTLD_NOW, RTLD_GLOBAL; 285 286 import std.path : buildPath; 287 288 /** String for containing last error message */ 289 private static string last_error = null; 290 291 version(OSX) { 292 /** 293 * Find the path to the libs on MacOS 294 * Returns: Path to the libs in the MacOS 295 */ 296 private static string getBasePath() { 297 import std.process : environment; 298 import std.path : buildPath; 299 300 static string path = null; 301 302 if (path !is null) return path; 303 304 path = environment.get("GTK_BASEPATH"); 305 if (path is null) { 306 path = environment.get("HOMEBREW_ROOT"); 307 if (path !is null) path = buildPath(path, "lib"); 308 } 309 310 return path; 311 } 312 313 private static string base_path = getBasePath(); 314 } 315 else { 316 private static string base_path = ""; 317 } 318 319 /** 320 * Load library by using OS-specific functions 321 * Params: 322 * library = Library's for loading name 323 * Returns: Loaded library if everything ok or null in other cases 324 */ 325 private static void * loadLibraryOS(string library) { 326 import std.path : buildPath; 327 328 void * handle = dlopen(cast(char *)toStringz(buildPath(base_path, library)), RTLD_GLOBAL | RTLD_NOW); 329 330 if (!handle) last_error = fromStringz(dlerror()).idup; 331 332 return handle; 333 } 334 335 /** 336 * Get symbol from library by using OS-specific functions 337 * Params: 338 * handle = A handle to the DLL module that contains the function or variable 339 * symbol = The function or variable name, or the function's ordinal value 340 * Returns: Address of symbol or null 341 */ 342 private static void * getSymbolOS(void * handle, string symbol) { 343 void * symbol_handle = dlsym(handle, cast(char *)toStringz(symbol)); 344 345 if (!symbol_handle) last_error = fromStringz(dlerror()).idup; 346 347 return symbol_handle; 348 } 349 350 /** 351 * Unload library by using OS-specific functions 352 * Params: 353 * lib = Library for unloading 354 * Returns: True if everything is ok 355 */ 356 private static bool unloadLibraryOS(void * lib) { 357 int res = dlclose(lib); 358 if (res != 0) last_error = fromStringz(dlerror()).idup; 359 return res == 0; 360 } 361 362 /** 363 * Get the last error message by using OS-specific functions 364 * Returns: String with error message or nothing 365 */ 366 private static string getLastErrorMessageOS() { 367 scope(exit) last_error = null; 368 return last_error; 369 } 370 } 371 }